
#include "luat_base.h"
#include "luat_fs.h"
#include "luat_spi.h"
#include "luat_malloc.h"

#define LUAT_LOG_TAG "vfs.lfs2"
#include "luat_log.h"

#ifdef LUAT_USE_FS_VFS

// #ifdef LUAT_VFS_USE_LFS2

#include "lfs.h"

FILE* luat_vfs_lfs2_fopen(void* userdata, const char *filename, const char *mode) {
    lfs_t* fs = (lfs_t*)userdata;
    lfs_file_t *file = (lfs_file_t*)luat_heap_malloc(sizeof(lfs_file_t));
    int flag = 0;
    for (size_t i = 0; i < strlen(mode); i++)
    {
        char m = *(mode + i);
        switch (m)
        {
        case 'r':
            flag |= LFS_O_RDONLY;
            break;
        case 'w':
            flag |= LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC;
            break;
        case 'a':
            flag |= LFS_O_APPEND;
            break;
        case '+':
            flag |= LFS_O_APPEND;
            break;
        
        default:
            break;
        }
    }
    int ret = lfs_file_open(fs, file, filename, flag);
    if (ret < 0) {
        luat_heap_free(file);
        return 0;
    }
    return (FILE*)file;
}

int luat_vfs_lfs2_getc(void* userdata, FILE* stream) {
    //LLOGD("posix_getc %p", stream);
    lfs_t* fs = (lfs_t*)userdata;
    lfs_file_t* file = (lfs_file_t*)stream;
    char buff = 0;
    int ret = lfs_file_read(fs, file, &buff, 1);
    if (ret < 0)
        return -1;
    return (int)buff;
}

int luat_vfs_lfs2_fseek(void* userdata, FILE* stream, long int offset, int origin) {
    lfs_t* fs = (lfs_t*)userdata;
    lfs_file_t* file = (lfs_file_t*)stream;
    int ret = lfs_file_seek(fs, file, offset, origin);
    return ret < 0 ? -1 : 0;
}

int luat_vfs_lfs2_ftell(void* userdata, FILE* stream) {
    lfs_t* fs = (lfs_t*)userdata;
    lfs_file_t* file = (lfs_file_t*)stream;
    int ret = lfs_file_tell(fs, file);
    return ret < 0 ? -1 : ret;
}

int luat_vfs_lfs2_fclose(void* userdata, FILE* stream) {
    lfs_t* fs = (lfs_t*)userdata;
    lfs_file_t* file = (lfs_file_t*)stream;
    lfs_file_close(fs, file);
    if (file != NULL)
        luat_heap_free(file);
    return 0;
}

int luat_vfs_lfs2_feof(void* userdata, FILE* stream) {
    lfs_t* fs = (lfs_t*)userdata;
    lfs_file_t* file = (lfs_file_t*)stream;
    if (lfs_file_size(fs, file) <= lfs_file_tell(fs, file))
        return 1;
    return 0;
}

int luat_vfs_lfs2_ferror(void* userdata, FILE *stream) {
    return 0;
}

size_t luat_vfs_lfs2_fread(void* userdata, void *ptr, size_t size, size_t nmemb, FILE *stream) {
    lfs_t* fs = (lfs_t*)userdata;
    lfs_file_t* file = (lfs_file_t*)stream;
    int ret = lfs_file_read(fs, file, ptr, size*nmemb);
    return ret < 0 ? 0 : ret;
}

size_t luat_vfs_lfs2_fwrite(void* userdata, const void *ptr, size_t size, size_t nmemb, FILE *stream) {
    lfs_t* fs = (lfs_t*)userdata;
    lfs_file_t* file = (lfs_file_t*)stream;
    int ret = lfs_file_write(fs, file, ptr, size*nmemb);
    return ret < 0 ? 0 : ret;
}

int luat_vfs_lfs2_remove(void* userdata, const char *filename) {
    lfs_t* fs = (lfs_t*)userdata;
    return lfs_remove(fs, filename);
}

int luat_vfs_lfs2_rename(void* userdata, const char *old_filename, const char *new_filename) {
    lfs_t* fs = (lfs_t*)userdata;
    return lfs_rename(fs, old_filename, new_filename);
}

int luat_vfs_lfs2_fexist(void* userdata, const char *filename) {
    FILE* fd = luat_vfs_lfs2_fopen(userdata, filename, "rb");
    if (fd) {
        luat_vfs_lfs2_fclose(userdata, fd);
        return 1;
    }
    return 0;
}

size_t luat_vfs_lfs2_fsize(void* userdata, const char *filename) {
    FILE *fd;
    size_t size = 0;
    fd = luat_vfs_lfs2_fopen(userdata, filename, "rb");
    if (fd) {
        size = lfs_file_size((lfs_t*)userdata, (lfs_file_t*)fd);
        luat_vfs_lfs2_fclose(userdata, fd);
    }
    return size;
}

int luat_vfs_lfs2_mkfs(void* userdata, luat_fs_conf_t *conf) {
    LLOGE("not support yet : mkfs");
    return -1;
}

int luat_vfs_lfs2_mount(void** userdata, luat_fs_conf_t *conf) {
    *userdata = (void*)conf->busname;
    return 0;
}

int luat_vfs_lfs2_umount(void* userdata, luat_fs_conf_t *conf) {
    LLOGE("not support yet : umount");
    return 0;
}

int luat_vfs_lfs2_mkdir(void* userdata, char const* _DirName) {
    return -1;
}

int luat_vfs_lfs2_rmdir(void* userdata, char const* _DirName) {
    return -1;
}

int luat_vfs_lfs2_info(void* userdata, const char* path, luat_fs_info_t *conf) {
    lfs_t* fs = (lfs_t*)userdata;
    memcpy(conf->filesystem, "lfs", strlen("lfs")+1);
    conf->type = 0;
    conf->total_block = fs->cfg->block_count;
    conf->block_used = lfs_fs_size(fs);
    conf->block_size = fs->cfg->block_size;
    return 0;
}

#define T(name) .name = luat_vfs_lfs2_##name

const struct luat_vfs_filesystem vfs_fs_lfs2 = {
    .name = "lfs2",
    .opts = {
        T(mkfs),
        T(mount),
        T(umount),
        T(mkdir),
        T(rmdir),
        T(remove),
        T(rename),
        T(fsize),
        T(fexist),
        T(info)
    },
    .fopts = {
        T(fopen),
        T(getc),
        T(fseek),
        T(ftell),
        T(fclose),
        T(feof),
        T(ferror),
        T(fread),
        T(fwrite)
    }
};

// #endif

#endif

